package view.overlay;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.VolatileImage;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;

import javax.swing.JComponent;

import view.Util;

/**
 * OverlayHost is a centralized location to store and manipulate Overlay
 * objects. It provides operations to add/remove Overlays as well as
 * create a composite image of them that can be used for drawing purposes.
 * 
 * @deviation
 * OverlayHost implements MouseMotionListener as well to forward
 * messages about fine-grained mouse movements to the Overlay
 * objects contained within.
 * 
 * 
 * @author matt
 *
 */
public class OverlayHost implements MouseListener, MouseMotionListener {
	
	private EnumMap<BorderLoc, List<Overlay>> mOverlays;
	
	private Image mBufferImage;
	
	// Size of the OverlayHost
	private int mWidth, mHeight;

	
	
	
	public OverlayHost() {
		this(null);
	}
	
	public OverlayHost(JComponent componentHost) {
		mOverlays = new EnumMap<BorderLoc, List<Overlay>>(BorderLoc.class);
		
		mBufferImage = Util.createImageBuffer(mWidth,  mHeight);
		
		if (componentHost != null) {
			componentHost.addMouseListener(this);
			componentHost.addMouseMotionListener(this);
		}
	}
	
	/**
	 * Retuns the composite image from all of the Overlays contained within
	 * the host.
	 * 
	 * TODO: make it composite the other border locations as well
	 * 
	 * @return	Image		The composited image.
	 */
	public Image getCompositeImage() {
		
		// For now, we create an image each time it is composited
//		mBufferImage = Util.createVolatileBuffer(mWidth, mHeight);
		
//		do {
			// If we don't have an image yet, return null
			if (mBufferImage == null) return (null);
			
			// Obtain a graphics object from the buffer
			Graphics2D g = (Graphics2D)mBufferImage.getGraphics();
			g.setColor(new Color(0, 0, 0, 0));
			g.clearRect(0, 0, mWidth, mHeight);
			
			
			// Composite the CENTER elements first
			List<Overlay> overlays = mOverlays.get(BorderLoc.CENTER);
			
			if (overlays == null) return (mBufferImage);
			
			
			for (Overlay o : overlays) {
				// Composite the images
				if (o.isVisible()) {
					g.drawImage(o.getImage(), o.getX(), o.getY(), null);
				}
			}
//		} while (mBufferImage.contentsLost());
		
		// Return the composited image
		return (mBufferImage);
	}
	
	/**
	 * Adds an Overlay to the OverlayHost
	 * @param overlay
	 * @param location
	 */
	public void addOverlay(Overlay overlay, BorderLoc location) {
		if (overlay == null) return;
		
		
		List<Overlay> listToAdd = mOverlays.get(location);
		
		
		Dimension dim = overlay.getPreferredSize();
		
		overlay.resize(dim.width, dim.height);
		
		if (listToAdd == null) {
			// no key for this BorderLoc, create one
			listToAdd = new ArrayList<Overlay>();
			listToAdd.add(overlay);
			mOverlays.put(location, listToAdd);
		}
		else {
			// Already exists as a key, just add to the list
			listToAdd.add(overlay);
		}
	
	}
	
	/**
	 * Remove an existing overlay from this host.
	 * 
	 * @param overlay
	 */
	public void removeOverlay(Overlay overlay) {
		for (BorderLoc loc : mOverlays.keySet()) {
			List<Overlay> list = mOverlays.get(loc);
			
			// if the list is null, no key exists for it, so ignore and continue
			if (list == null) continue;
			
			// remove it from the list
			list.remove(overlay);
			
			
		}
	}
	
	
	/**
	 * Resize the OverlayHost, and by proxy, all the children Overlay objects
	 * contained within.
	 * 
	 * @param width		int		The new width.
	 * @param height	int		The new height.
	 */
	public void resize(int width, int height) {
		for (BorderLoc loc : mOverlays.keySet()) {
			List<Overlay> list = mOverlays.get(loc);
			
			if (list == null) continue;
			
			for (Overlay o : list) {
				o.resize(width, height);
			}
		}
		
		// resize the host
		this.mWidth = width;
		this.mHeight = height;
		
		// Create a new image buffer
		mBufferImage = Util.createImageBuffer(width, height);
	}
	
	
	
	/*
	 * ******************************************************
	 * 		Mouse Listener Functions
	 * ******************************************************
	 */

	/**
	 * <code>mouseClicked</code> forwards all the mouse click events
	 * to the Overlay objects contained within the host.
	 */
	@Override
	public void mouseClicked(MouseEvent e) {
		for (BorderLoc loc : mOverlays.keySet()) {
			List<Overlay> list = mOverlays.get(loc);
			
			if (list != null) {
				for (Overlay o : list) {
					o.mouseClicked(e);
				}
			}
		}
		
	}

	/**
	 * <code>mouseEntered</code> forwards all the mouse entered events
	 * to the Overlay objects contained within the host.
	 */
	@Override
	public void mouseEntered(MouseEvent e) {
		for (BorderLoc loc : mOverlays.keySet()) {
			List<Overlay> list = mOverlays.get(loc);
			
			if (list != null) {
				for (Overlay o : list) {
					o.mouseEntered(e);
				}
			}
		}
		
	}

	/**
	 * <code>mouseExited</code> forwards all the mouse exited events
	 * to the Overlay objects contained within the host.
	 */
	@Override
	public void mouseExited(MouseEvent e) {
		for (BorderLoc loc : mOverlays.keySet()) {
			List<Overlay> list = mOverlays.get(loc);
			
			if (list != null) {
				for (Overlay o : list) {
					o.mouseExited(e);
				}
			}
		}
		
	}

	/**
	 * <code>mousePressed</code> forwards all the mouse pressed events
	 * to the Overlay objects contained within the host.
	 */
	@Override
	public void mousePressed(MouseEvent e) {
		for (BorderLoc loc : mOverlays.keySet()) {
			List<Overlay> list = mOverlays.get(loc);
			
			if (list != null) {
				for (Overlay o : list) {
					o.mousePressed(e);
				}
			}
		}
		
	}

	/**
	 * <code>mouseReleased</code> forwards all the mouse released events
	 * to the Overlay objects contained within the host.
	 */
	@Override
	public void mouseReleased(MouseEvent e) {
		for (BorderLoc loc : mOverlays.keySet()) {
			List<Overlay> list = mOverlays.get(loc);
			
			if (list != null) {
				for (Overlay o : list) {
					o.mouseReleased(e);
				}
			}
		}
		
	}

	@Override
	public void mouseDragged(MouseEvent e) {
		for (BorderLoc loc : mOverlays.keySet()) {
			List<Overlay> list = mOverlays.get(loc);
			
			if (list != null) {
				for (Overlay o : list) {
					o.mouseDragged(e);
				}
			}
		}
		
	}

	@Override
	public void mouseMoved(MouseEvent e) {
		for (BorderLoc loc : mOverlays.keySet()) {
			List<Overlay> list = mOverlays.get(loc);
			
			if (list != null) {
				for (Overlay o : list) {
					o.mouseMoved(e);
				}
			}
		}
		
	}
	
	
	
	

	

}
